package org.mobai.flow.strategy.impl;

import cn.hutool.core.util.ObjectUtil;
import lombok.AllArgsConstructor;
import org.camunda.bpm.model.bpmn.builder.AbstractFlowNodeBuilder;
import org.camunda.bpm.model.bpmn.builder.ExclusiveGatewayBuilder;
import org.camunda.bpm.model.bpmn.builder.MultiInstanceLoopCharacteristicsBuilder;
import org.camunda.bpm.model.bpmn.builder.UserTaskBuilder;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaFormField;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperties;
import org.camunda.bpm.model.bpmn.instance.camunda.CamundaProperty;
import org.camunda.bpm.model.xml.ModelInstance;
import org.mobai.flow.constant.CamundaConstant;
import org.mobai.flow.enums.ApprovalMultiEnum;
import org.mobai.flow.enums.AssigneeTypeEnum;
import org.mobai.flow.enums.ImplementationEnum;
import org.mobai.flow.enums.NobodyTypeEnum;
import org.mobai.flow.pojo.bpmn.FormProperty;
import org.mobai.flow.pojo.bpmn.Listener;
import org.mobai.flow.pojo.bpmn.Node;
import org.mobai.flow.pojo.bpmn.node.UserTask;
import org.mobai.flow.strategy.BpmnStrategy;
import org.mobai.flow.strategy.context.BpmnBuildContext;
import org.mobai.flow.utils.FlowUtil;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

import static org.mobai.flow.enums.AssigneeTypeEnum.*;

/**
 * @author hyk
 */
@AllArgsConstructor
@Service
public class UserTaskBpmnImpl implements BpmnStrategy<UserTask> {
    private BpmnBuildContext bpmnBuildContext;


    /**
     * 根据提供的UserTask对象构建BPMN流程节点
     *
     * @param flowNodeBuilder 流程节点构建器，用于构建BPMN流程节点
     * @param node            UserTask对象，包含要构建的流程节点的详细信息
     * @return 返回一个构建后的AbstractFlowNodeBuilder对象，用于继续构建流程
     */
    @Override
    public AbstractFlowNodeBuilder build(AbstractFlowNodeBuilder flowNodeBuilder, UserTask node, Map<String, String> nodeMap) {
        // 获取UserTask构建器实例
        UserTaskBuilder userTaskBuilder = flowNodeBuilder.userTask();

        // 设置用户任务的名称
        userTaskBuilder.name(node.getName());

        // 设置用户任务的ID
        userTaskBuilder.id(node.getId());

        // 设置审批人，使用表达式语言引用审批人变量
        userTaskBuilder.camundaAssignee("${" + node.getId() + "_Approval}");

        // 设置审批人，根据node对象中的信息，调用setApprovalMulti方法
        setApprovalMulti(node, userTaskBuilder);

        // 设置执行监听器，根据node对象中的执行监听器列表，调用setCamundaExecutionListener方法
        setCamundaExecutionListener(userTaskBuilder, node.getExecutionListeners());

        // 获取执行监听器列表，保存在executionListeners变量中
        List<Listener> executionListeners = node.getExecutionListeners();

        // 设置任务监听器，根据executionListeners列表中的监听器，调用setCamundaTaskListener方法
        setCamundaTaskListener(executionListeners, userTaskBuilder);

        // 设置表单字段，根据node对象中的表单属性，调用setCamundaFormFieldAndProperty方法
        setCamundaFormFieldAndProperty(node.getFormProperties(), userTaskBuilder);

        // 设置审批人为空的操作
//        setCamundaEmptyOperation(node, userTaskBuilder);
        // 设置操作权限，根据node对象中的操作权限列表，调用setOperations方法
        setOperations(node.getOperations(), userTaskBuilder, camundaProperties -> {
            // 设置无人审批人操作
            setNobodyApproved(node, camundaProperties, userTaskBuilder);

        });

        AbstractFlowNodeBuilder flowNode = userTaskBuilder;

        String backNodeId = node.getOperations().getBackNodeId();
        if (ObjectUtil.isNotEmpty(backNodeId)) {
            ExclusiveGatewayBuilder exclusiveGatewayBuilder = userTaskBuilder.exclusiveGateway();
            exclusiveGatewayBuilder.condition("驳回", CamundaConstant.MOBAI_FLOW_BACK).connectTo(backNodeId);
            flowNode = exclusiveGatewayBuilder;
        }

        // 检查node对象的子节点是否为空
        Node child = node.getChild();
        if (ObjectUtil.isNotNull(child)) {


            // 如果子节点不为空，则调用bpmnBuildContext对象的buildBpmn方法继续构建流程
            return bpmnBuildContext.buildBpmn(flowNode, child, nodeMap);
        }

        // 如果子节点为空，则返回userTaskBuilder对象
        return flowNode;
    }

    /**
     * 设置无人批准的用户任务配置
     * 当用户任务被配置为无人批准时，根据不同的无人批准类型和用户列表，设置相应的Camunda属性
     *
     * @param node              用户任务节点，包含无人批准类型和用户列表信息
     * @param camundaProperties Camunda属性集合，用于添加无人批准相关的属性
     * @param userTaskBuilder   用户任务构建器，用于获取元素并创建Camunda属性实例
     */
    private static void setNobodyApproved(UserTask node, CamundaProperties camundaProperties, UserTaskBuilder userTaskBuilder) {
        // 根据用户任务节点的无人批准类型获取对应的无人批准类型枚举
        NobodyTypeEnum nobodyTypeEnum = NobodyTypeEnum.getByType(node.getNobody());
        // 如果无人批准类型枚举为空，则直接返回，不进行后续操作
        if (ObjectUtil.isNull(nobodyTypeEnum)) {
            return;
        }
        // 获取无人批准用户列表
        List<String> nobodyUsers = node.getNobodyUsers();
        // 获取用户任务构建器的元素对应的模型实例
        ModelInstance modelInstance = userTaskBuilder.getElement().getModelInstance();
        // 创建一个新的Camunda属性实例，用于存储无人批准类型
        CamundaProperty nobodyProperty = modelInstance.newInstance(CamundaProperty.class);
        // 设置无人批准类型的属性名称
        nobodyProperty.setCamundaName(CamundaConstant.OPERATION_NOBODY);
        // 设置无人批准类型的属性值
        nobodyProperty.setCamundaValue(nobodyTypeEnum.getType());
        // 将无人批准类型的属性添加到Camunda属性集合中
        camundaProperties.addChildElement(nobodyProperty);

        // 如果无人批准用户列表非空
        if (ObjectUtil.isNotEmpty(nobodyUsers)) {
            // 创建一个新的Camunda属性实例，用于存储无人批准用户列表
            CamundaProperty nobodyUsersProperty = modelInstance.newInstance(CamundaProperty.class);
            // 设置无人批准用户列表的属性名称
            nobodyUsersProperty.setCamundaName(CamundaConstant.OPERATION_NOBODY_USERS);
            // 将无人批准用户列表转换为逗号分隔的字符串，并设置为属性值
            nobodyUsersProperty.setCamundaValue(String.join(",", nobodyUsers));
            // 将无人批准用户列表的属性添加到Camunda属性集合中
            camundaProperties.addChildElement(nobodyUsersProperty);
        }
    }


    /**
     * 设置审批方式
     *
     * @param node            审批节点信息，包含审批方式等信息
     * @param userTaskBuilder 用户任务构建器，用于构建用户任务节点
     */
    private void setApprovalMulti(UserTask node, UserTaskBuilder userTaskBuilder) {
        // 根据节点的审批方式multi获取对应的审批类型
        ApprovalMultiEnum multi = ApprovalMultiEnum.getByMulti(node.getMulti());
        // 如果审批类型不存在，则抛出运行时异常
        if (ObjectUtil.isNull(multi)) {
            throw new RuntimeException("审批方式错误");
        }
        // 创建并配置循环特性构建器
        MultiInstanceLoopCharacteristicsBuilder multiInstanceLoopCharacteristicsBuilder = userTaskBuilder.multiInstance();
        // 设置camunda集合属性
        setCamundaCollection(node, multiInstanceLoopCharacteristicsBuilder);
        // 根据不同的审批类型设置相应的循环方式和完成条件
        switch (multi) {
            case SEQUENTIAL:
                // 顺序审批，一个接一个地完成任务
                multiInstanceLoopCharacteristicsBuilder.sequential().completionCondition("${multiInstanceCompleteTask.completeTask(execution," + Double.parseDouble("100") + ")}");
                break;
            case JOINT:
                // 会签审批，满足一定比例的完成条件后即可完成任务
                multiInstanceLoopCharacteristicsBuilder.parallel().completionCondition("${multiInstanceCompleteTask.completeTask(execution," + Double.parseDouble(node.getMultiPercent()) + ")}");
                break;
            case SINGLE:
                // 单签审批，只要有任一任务完成即可结束
                multiInstanceLoopCharacteristicsBuilder.parallel().completionCondition("${multiInstanceCompleteTask.completeTask(execution," + Double.parseDouble("0") + ")}");
                break;
        }
    }


    /**
     * 设置审批人集合
     * 本方法用于根据任务节点的类型（用户或角色）设置Camunda流程的审批人集合
     * 主要用于处理多实例循环特性
     *
     * @param node                                    任务节点，包含审批人类型和具体的审批人信息
     * @param multiInstanceLoopCharacteristicsBuilder 多实例循环特性构建器，用于设置Camunda流程中的多实例执行逻辑
     */
    private void setCamundaCollection(UserTask node, MultiInstanceLoopCharacteristicsBuilder multiInstanceLoopCharacteristicsBuilder) {
        // 根据节点的审批人类型获取对应的枚举类型
        AssigneeTypeEnum assigneeTypeEnum = AssigneeTypeEnum.getByType(node.getAssigneeType());
        // 如果审批人类型无效，则直接返回，不进行后续操作
        if (ObjectUtil.isNull(assigneeTypeEnum)) {
            return;
        }
        // 设置多实例元素变量，用于在流程中传递审批信息
        multiInstanceLoopCharacteristicsBuilder.camundaElementVariable(node.getId() + "_Approval");

        // 根据审批人类型设置不同的审批集合
        switch (assigneeTypeEnum) {
            case USER:
                // 获取分配给任务的用户列表
                List<String> users = node.getUsers();
                // 如果用户列表为空，则抛出运行时异常
                if (ObjectUtil.isNull(users)) {
                    throw new RuntimeException("用户不能为空");
                }
                // 设置审批集合为特定脚本格式的用户列表
                multiInstanceLoopCharacteristicsBuilder.camundaCollection(String.format(USER.getScript(), users.stream().collect(Collectors.joining("','", "'", "'"))));
                break;
            case ROLE:
                // 获取分配给任务的角色列表
                List<String> roles = node.getRoles();
                // 如果角色列表为空，则抛出运行时异常
                if (ObjectUtil.isNull(roles)) {
                    throw new RuntimeException("角色不能为空");
                }
                // 设置审批集合为特定脚本格式的角色列表
                multiInstanceLoopCharacteristicsBuilder.camundaCollection(String.format(ROLE.getScript(), roles.stream().collect(Collectors.joining("','", "'", "'"))));
                break;
            case SELF:
                // 如果审批人类型是自己，则设置审批集合为特定脚本格式的当前用户的ID
                multiInstanceLoopCharacteristicsBuilder.camundaCollection(String.format(SELF.getScript()));
                break;
            default:
                // 如果审批人类型不是用户或角色，则不执行任何操作
        }
    }


    /**
     * 设置任务监听器
     *
     * @param executionListeners
     * @param userTaskBuilder
     */
    private void setCamundaTaskListener(List<Listener> executionListeners, UserTaskBuilder userTaskBuilder) {
        Optional.ofNullable(executionListeners).ifPresent(listeners -> listeners.forEach(listener -> {
            switch (ImplementationEnum.getByType(listener.getImplementationType())) {
                case CLASS:
                    userTaskBuilder.camundaTaskListenerClass(listener.getEvent(), listener.getImplementation());
                    break;
                case DELEGATE_EXPRESSION:
                    userTaskBuilder.camundaTaskListenerDelegateExpression(listener.getEvent(), listener.getImplementation());
                    break;
                default:
                    userTaskBuilder.camundaTaskListenerExpression(listener.getEvent(), listener.getImplementation());
                    break;
            }
        }));
    }


    /**
     * 设置表单字段属性
     *
     * @param formPropertiesList 表单字段属性列表，可能为空
     * @param userTaskBuilder    用户任务构建器，用于构建带有表单字段的用户任务
     *                           本方法通过遍历formPropertiesList，为每个表单字段创建CamundaFormField实例，并设置相应的属性，
     *                           然后调用FlowUtil类中的setCamundaProperties方法，进一步设置表单字段的属性。
     */
    private void setCamundaFormFieldAndProperty(List<FormProperty> formPropertiesList, UserTaskBuilder userTaskBuilder) {
        // 对formPropertiesList进行空值检查，如果非空则执行下面的代码
        Optional.ofNullable(formPropertiesList).ifPresent(formProperties -> formProperties.forEach(formProperty -> {
            // 创建CamundaFormField实例并设置字段ID，标签和类型
            CamundaFormField camundaFormField = userTaskBuilder.camundaFormField().camundaId(formProperty.getId()).camundaLabel(formProperty.getName()).camundaType("string").getElement();
            // 调用FlowUtil类的方法，为camundaFormField设置更多基于formProperty的属性
            FlowUtil.setCamundaProperties(formProperty, camundaFormField);
        }));
    }


}
